package com.suprema.biomini;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;

/**
 * 指紋取樣數值資料
 * @author Kent Yeh
 */
public class Template implements Externalizable {

    private static final long serialVersionUID = -9091921997255817507L;
    private String defaultIdentity = "!@|";
    byte[] data = new byte[SFR300.SF_TEMPLATESIZE];
    int size = 0;
    private String identity = defaultIdentity;
    private int index = 0;

    public Template() {
        super();
        clear();
    }

    /**
     * @param identity  識別文字
     * @param index  自定之識別索引
     */
    public Template(String identity, int index) {
        super();
        setIdentity(identity);
        this.index = index;
        clear();
    }

    /**
     * 設定此資料之識別
     * @param identity 識別文字
     * @return 本資料物件
     */
    public Template setIdentity(String identity) {
        this.identity = identity == null || identity.isEmpty() ? defaultIdentity : identity;
        return this;
    }

    /**
     * 取得設定之識別文字
     * @return 
     */
    public String getIdentity() {
        return defaultIdentity.equals(identity) ? null : identity;
    }

    /**
     * 自定之識別索引
     * @return 識別索引
     */
    public int getIndex() {
        return index;
    }

    /**
     * 設定之識別索引
     * @param index 識別索引
     * @return 本資料物件
     */
    public Template setIndex(int index) {
        this.index = index;
        return this;
    }

    /**
     * 取得二進位指紋資料
     * @return 
     */
    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    /**
     * 取回樣本資料大小
     * @return 樣本資料大小
     */
    public int getSize() {
        return size;
    }

    /**
     * 設定樣本資料大小
     * @param size 樣本資料大小 
     */
    public void setSize(int size) {
        this.size = size;
    }

    /**
     * 取回第幾位的資料數值
     * @param index 索引
     * @return 
     */
    public int getByte(int index) {
        assert index > -1 && index < data.length : String.format("索引(%d)必須介於 0 ~ %,d", index, data.length);
        return data[index] & 0xff;
    }

    /**
     * 設定資料位元(bit)
     * @param index 資料索引
     * @param value 設定值，API使用資料型態為unsigned byte，其值必須介於 0 ~ 255
     */
    public void setByte(int index, int value) {
        assert index > -1 && index < size : String.format("索引值必須介於 0 ~ %d", data.length);
        assert value > -1 && value < 256 : "unsigned byte 設值必須介於 0~255 ";
        if (value > 0 && index > size) {
            setSize(size);
        }
        data[index] = (byte) (0x000000F & value);
    }

    /**
     * 取回第幾位的16進位資料數值
     * @param index  索引
     * @return 16進位數值資料
     */
    public String getHex(int index) {
        return String.format("%02x", getByte(index));
    }

    /**
     * 清除指紋資料
     */
    public void clear() {
        Arrays.fill(data, (byte) 0);
        size = 0;
    }

    @Override
    protected Template clone() {
        Template result = new Template();
        result.setIdentity(identity);
        result.setIndex(index);
        result.data = Arrays.copyOf(this.data, this.data.length);
        result.setSize(this.size);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Template other = (Template) obj;
        if (!Arrays.equals(this.data, other.data)) {
            return false;
        }
        if (this.size != other.size) {
            return false;
        }
        if ((this.identity == null) ? (other.identity != null) : !this.identity.equals(other.identity)) {
            return false;
        }
        if (this.index != other.index) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Arrays.hashCode(this.data);
        hash = 97 * hash + this.size;
        hash = 97 * hash + (this.identity != null ? this.identity.hashCode() : 0);
        hash = 97 * hash + this.index;
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (getIdentity() != null) {
            sb.append(getIdentity()).append(" - ").append(getIndex()).append(' ');
        }
        if(sb.length()==0){
            sb.append("[").append(size).append("個位元組]");
        }
        return sb.toString();
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(identity);
        out.writeInt(index);
        out.writeInt(size);
        out.write(data, 0, size);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        identity = (String) in.readObject();
        index = in.readInt();
        Arrays.fill(data, (byte) 0);
        size = in.readInt();
        in.read(data, 0, size);
    }

    /**
     * 顯示二進位資料內容
     * @return 展示內容文字
     */
    public String showBytes() {
        return showBytes(true);
    }

    /**
     * 顯示二進位資料內容
     * @param hex 是否以16進位顯示內容
     * @return 展示內容文字
     */
    public String showBytes(boolean hex) {
        StringBuilder sb = new StringBuilder();
        int l = 0;
        for (int i = 0; i < size; i++) {
            if (hex) {
                if (i % 16 == 0) {
                    sb.append(String.format("\n%2x: ", l++));
                } else if (i % 8 == 0) {
                    sb.append(": ");
                }
                sb.append(getHex(i)).append(" ");
            } else {
                if (i % 10 == 0) {
                    sb.append(String.format("\n%3d: ", l++));
                }
                sb.append(String.format("%3d", getByte(i))).append(" ");
            }
        }
        return sb.toString();
    }
}
